require "gosu"
require "mouse.rb"
require "button.rb"
require "carray.rb"

#The GameWindow class is the central hub of the program. It is utimately responsible for all input and output (exept for logging output). 
#The main game loops can be found in this class.
class GameWindow < Gosu::Window
  Static=true
  Game=false  
  attr_accessor :cursor,:player,:characters,:images,:mapimages,:background_image,:guiWidgets,:camera,:map
  
  def initialize(fullscreen,state=Game,staticObj=nil)
    super(640, 480, fullscreen, 20)
    self.caption = "The Legend of Zelda"
    $window=self #pre initialize global var to allow win initialization
    @background_image = Gosu::Image.new(self, "null.png", true)
    @cursor = Cursor.new("img/gui/cursor.bmp", false)
        $animations = {}
        $textbar=nil
        $yesnoreply=nil
        $console=nil
        $trace=nil
        $framedrawn=false
        $timer=0
    @fading=false
    @fadeImg=nil
    @fadeAlpha=255
    @level=""
    Environment.initializeWorld
    @player = Player.new("Link")
    @player.warp(320,240)
    @camera=Camera.new
    @images = CArray.new
    @mapimages = CArray.new
    @characters = CArray.new
    @guiWidgets = CArray.new
    @cmds=nil
    @state=Game
    @focused=nil
    @stickfocused=nil
    @leftdrag = MouseDragEvent.new
    @rightdrag = MouseDragEvent.new
    if not @state 
      @staticObj=staticObj
    else
      @staticObj=nil
    end
    RubyScript::Utils.RunEvent("Start Up")
    StartMenu.new.start
    @map=Map.new
    @level="null"
    $window=self
  end
  
  def currentLevel
    return @level
  end
  
  def stateObject
    return @staticObj
  end
  def menu
    return @staticObj
  end
  
  def dump
    if $debug
      f=File.new("logs/dump/window.dump","w")
      f.write Time.new.to_s+"\n\n"
      f.write("Player: "+@player.name+"\n")
      f.write("Focused: "+@focused.to_s+"\n")
      f.write("Level: "+@level+"\n")
      Animation::dump
      f=File.new("logs/dump/images.dump","w")
      f.write Time.new.to_s+"\n\n"
      @images.each {|image|
        f.write "ID: "+image.id.to_s+", Src: "+image.src.to_s+", X: "+image.x.to_s+", Y: "+image.y.to_s+", Z: "+image.z.to_s+", Tracer: "+image.descr.to_s+"\n"
      }
      f.close
      f=File.new("logs/dump/widgets.dump","w")
      f.write Time.new.to_s+"\n\n"
      @guiWidgets.each {|widget|
        f.write "Name: "+widget.name.to_s+", ID: "+widget.id.to_s+", Type: "+widget.class.to_s+", Button: "+widget.buttonId.to_s+", Def-Image: "+widget.defimage.to_s+", ZFocus: "+widget.zfocus.to_s+", Sleeping: "+widget.sleeping.to_s+"\n"
      }
      f.close
      f=File.new("logs/dump/buttons.dump","w")
      f.write Time.new.to_s+"\n\n"
      $textbar.clearText
      $textbar.hide
      $textbar.queText("Data dump complete.")
      $textbar.wakeUp      
    end
  end
  
  def button_down(id)
    if @state 
      @staticObj.button_down(id)
      checkFocus
      widgetDownProc(id)
      return
    end
    char=button_id_to_char(id)
    if char=~/[A-Za-z0-9]/i
      if char=="z"
        @player.actionz
      elsif char=="x"
        @player.actionx
      elsif char=="c"
        @player.actionc
      end
    end
    checkFocus
    widgetDownProc(id)
  end
  
  def button_up(id)
    if @state
      @staticObj.button_up(id)
      checkFocus                       
      widgetUpProc(id)                        
      return                              
    end      
    char=button_id_to_char(id)
    if id == Gosu::Button::KbEscape
      $flog.info("Exit Cmd") {"Terminal: ButtonDown(KbEsc)"}
      close
    elsif id == Gosu::Button::KbEnd
      if $debug
        raise
      end
    elsif id == Gosu::Button::KbInsert
      @cursor.toggleVisible
    elsif id == Gosu::Button::KbSpace
      @player.tryTalk
    elsif char == "`" 
      if $debug or DebugConsole.enabled
        if $console.sleeping?
          $console.wakeUp
        else
          $console.sleep
        end
      end
    end
    checkFocus                       
    widgetUpProc(id)   
  end
    
  def setStatic(staticObj)
    @staticObj=staticObj
    @state=Static
  end
  
  def setGame
    @staticObj=nil
    @state=Game
  end
  
  def fadeIn
    @fading=true
    @fadeAlpha=255
    @fadeImg=Gosu::Image.new(self,"img/banners/black.png",true)    
  end
  
  def newCharacter(char)
    return @characters << char
  end
  
  def newImage(x,y,z,src,hardBorders=true,tracer="None")
    img=Image.new(x,y,z,src,hardBorders,tracer,@images.size)
      #must explicitly define position to avoid overwrites
    return @images << img
  end
  
  def setImage(index,x,y,z,src,hardBorders=true,tracer="None")
    img=Image.new(x,y,z,src,@images.size,hardBorders,tracer)
      #must explicitly define position to avoid overwrites
    @images[index]=img
    return index
  end
  
  def deleteImage(no)
    if @images[no]!=nil
      @images[no].del
    end
      @images.delete_at(no)
  end
  
  def deleteAllImages
    @images.clear
  end
  
  def setBackgroundImage(src)
    @background_image = Gosu::Image.new(self,src,true)    
  end
  
  def setMapBG(src)
    @map_image = Gosu::Image.new(self,src,true)    
  end
  
  def newMapImage(x,y,z,src,hardBorders=true,tracer="None")
    img=Image.new(x,y,z,src,hardBorders,tracer,@mapimages.size)
    #must explicitly define position to avoid overwrites
    return @mapimages << img
  end
  
  def deleteMapImage(no)
    if @mapimages[no]!=nil
      @mapimages[no].del
    end
      @mapimages.delete_at(no)
  end
  
  def deleteAllMapImages
    @mapimages.clear
  end  
  
  def newGuiWidget(widget)
    return @guiWidgets << widget
  end
  
  def deleteGuiWidget(widget)
    id=widget.id
    @guiWidgets.delete_at(id)
  end
  
  def deleteAllWidgets
    @guiWidgets.each {|widget| widget.del}
    @guiWidgets.clear
    @focused=nil
    @stickfocused=nil
    self.text_input=nil
  end
  
  def createWidgets
    $textbar=nil
    $textbar=TextBar.new("Textbar")
    $textbar.sleep
    if DebugConsole.enabled or $debug
      $console=DebugConsole.new("Console")
      $console.sleep
    end
    if TracePanel.enabled
      $trace=TracePanel.new("Trace Panel")
      $trace.sleep
    end
    $healthmeter=HealthMeter.new("Player Health Meter", 530, 10)
    $healthmeter.wakeUp
    $magicmeter=MagicMeter.new("Player Magic Meter", 10, 200)
    $magicmeter.wakeUp
    self.text_input=nil
    @focused=nil
    @stickfocused=nil
  end
  
  def setTextInput(field)
    self.text_input=field
  end
  
  def setFocus(object)
    if @focused!=nil and not @focused.sleeping? #dont blur the object if it is the window or if it is sleeping
      @focused.blur
    end
    if object==nil
      @focused=nil
    else
      @focused=@guiWidgets[object]
    end
  end
  
  def setStickyFocus(object)
    if @stickfocused!=nil
      @stickfocused.onStickyBlur
    end
    @stickfocused=object
    if @stickfocused!=nil
      @stickfocused.onStickyFocus
    end
  end
  
  def hasFocus
    return @focused
  end
  
  def hasStickyFocus
    return @stickfocused
  end
  
  def widgetDownProc(id)
    char=button_id_to_char(id)
    setStickyFocus(nil) if (id==Gosu::Button::MsLeft or id==Gosu::Button::MsRight) and @stickfocused!=nil #and not @stickfocused.contains?(mouse_x, mouse_y)
    if @stickfocused!=nil and not @stickfocused.sleeping? #only sticky focused widgets recieve text input 
      if id==Gosu::Button::MsLeft
        @leftdrag.start
      elsif id==Gosu::Button::MsRight
        @rightdrag.start
      elsif char!=nil
        if button_down?(Gosu::Button::KbLeftShift) or button_down?(Gosu::Button::KbRightShift)
          if char=~/[A-Z]/i
            char=char.upcase!
          else
            capschar=WidgetTextCaps[char]
            if capschar!=nil
              char=capschar
            end
          end
        end
        @stickfocused.feedText(char)
      end  
    end
  end
  
  def widgetUpProc(id)
    char=button_id_to_char(id)
    setStickyFocus(nil) if (id==Gosu::Button::MsLeft or id==Gosu::Button::MsRight) and @stickfocused!=nil #and not @stickfocused.contains?(mouse_x, mouse_y)
    if @stickfocused!=nil and not @stickfocused.sleeping? #only sticky focused widgets recieve text input 
      if id==Gosu::Button::MsLeft
        if @leftdrag.inprogress
          @leftdrag.end
          @stickfocused.event(WidgetEvent::MsLeftDrag)          
        else
          @leftdrag.terminate
          @stickfocused.event(WidgetEvent::MsLeft)
        end
      elsif id==Gosu::Button::MsRight
        if @rightdrag.inprogress
          @rightdrag.end
          @stickfocused.event(WidgetEvent::MsRightDrag)                  
        else
          @rightdrag.terminate
          @stickfocused.event(WidgetEvent::MsRight)
        end
      elsif id==Gosu::Button::KbUp or id==Gosu::Button::GpUp
        @stickfocused.event(WidgetEvent::KeyUp)
      elsif id==Gosu::Button::KbDown or id==Gosu::Button::GpDown
        @stickfocused.event(WidgetEvent::KeyDown)
      elsif id==Gosu::Button::KbLeft or id==Gosu::Button::GpLeft
        @stickfocused.event(WidgetEvent::KeyLeft)
      elsif id==Gosu::Button::KbRight or id==Gosu::Button::GpRight
        @stickfocused.event(WidgetEvent::KeyRight)
      elsif id==Gosu::Button::KbRight or id==Gosu::Button::GpRight
        @stickfocused.event(WidgetEvent::KeyRight)
      elsif id==Gosu::Button::KbBackspace or id==Gosu::Button::KbDelete
        @stickfocused.event(WidgetEvent::KeyDelete)   
      elsif id==Gosu::Button::KbEscape or id==Gosu::Button::GpButton7
        @stickfocused.event(WidgetEvent::KeyEscape)     
      elsif id==Gosu::Button::KbEnter or id==Gosu::Button::GpButton2 or id==28 #strange Gosu bug, KbEnter!=Enter code
        @stickfocused.event(WidgetEvent::KeyEnter)
      elsif id==Gosu::Button::KbLeftAlt or id==Gosu::Button::KbRightAlt or id==Gosu::Button::GpButton5
        @stickfocused.event(WidgetEvent::KeyAlt)
      elsif id==Gosu::Button::KbLeftControl or id==Gosu::Button::KbRightControl or id==Gosu::Button::GpButton3
        @stickfocused.event(WidgetEvent::KeyControl)
      elsif id==Gosu::Button::KbLeftShift or id==Gosu::Button::KbRightShift or id==Gosu::Button::GpButton6
        @stickfocused.event(WidgetEvent::KeyShift)      
      elsif id==Gosu::Button::KbSpace or id==Gosu::Button::KbSpace or id==Gosu::Button::GpButton4
        @stickfocused.event(WidgetEvent::KeySpace)
      elsif id==Gosu::Button::GpButton0
        @stickfocused.event(WidgetEvent::GpEnter)
      elsif id==Gosu::Button::GpButton1
        @stickfocused.event(WidgetEvent::GpCancel)        
      end
    end
    if @focused!=nil and not @focused.sleeping? and (@stickfocused==nil or @focused.id != @stickfocused.id) #normally focused widgets only recieve click input 
      if id==Gosu::Button::MsLeft
        @focused.event(WidgetEvent::MsLeft)
      elsif id==Gosu::Button::MsRight
        @focused.event(WidgetEvent::MsRight)
      elsif id==Gosu::Button::GpButton0
        @focused.event(WidgetEvent::GpEnter)
      end
    end
  end
  
  def checkFocus
    if @focused==nil
      oldfocus=nil
    else
      oldfocus=@focused.id      
    end
    newfocus=nil
    focusFlag=false
    @guiWidgets.each { |widget|
      if (@cursor.visible and widget.over?(mouse_x,mouse_y)) and not widget.sleeping?
        if not focusFlag
          newfocus=widget.id
          focusFlag=true
        elsif widget.zfocus >= @guiWidgets[newfocus].zfocus #give focus to the widget with the highest focus zorder, or in the event of a tie, give focus to the new object
          newfocus=widget.id
        end
      else
      end
    }
    if newfocus!=nil and newfocus!=oldfocus
      @guiWidgets[newfocus].focus
     # $flog.debug("Widget Focus") {"To: "+@focused.id.to_s}
    elsif newfocus==nil and oldfocus!=nil
      setFocus(nil)
      $flog.debug("Widget Focus") {"To: Window"}
    end
    #puts @focused.to_s+","+newfocus.to_s+","+oldfocus.to_s
  end
  
 # def close
 #   $flog.info("Exit Cmd") {"Terminal: Window GUI Interface(Close Button)"}
 #   super
 # end
  
  def loadLevel(level) #to replace newEnvironment
    @level=level
    Environment::Map::GUILoad(level)
  end
  
  def newEnvironment(level)
    Environment::Map::GUILoad(level)
  end
  
  def update
    checkFocus
    
    if @leftdrag.starting
      if(Gosu::distance(@leftdrag.start_x, @leftdrag.start_y, mouse_x, mouse_y) > 5)
        @leftdrag.confirmStart
      end
    elsif @rightdrag.starting
      if(Gosu::distance(@rightdrag.start_x, @rightdrag.start_y, mouse_x, mouse_y) > 5)
        @rightdrag.confirmStart
      end
    end
    
    if @state
      @staticObj.update
      return
    end
    if @focused==nil
      #if only one of a group of commands can be executed at a given time, group them into them same if statment
      if button_down? Gosu::Button::KbLeftControl or button_down? Gosu::Button::KbRightControl or button_down? Gosu::Button::GpButton0
        @player.toggleSpeed(Velocity::Running)
      end
      
      if button_down? Gosu::Button::KbLeft or button_down? Gosu::Button::GpLeft then
        @player.moveLeft
      elsif button_down? Gosu::Button::KbRight or button_down? Gosu::Button::GpRight then
        @player.moveRight
      elsif button_down? Gosu::Button::KbDown or button_down? Gosu::Button::GpDown then
        @player.moveForward
      elsif button_down? Gosu::Button::KbUp or button_down? Gosu::Button::GpUp then
        @player.moveBackward
      else
        @player.stand
      end
     # if Gosu::milliseconds%20
        @player.move
     # end
    end
    @characters.each { |char|
      #char.runAI
      char.move
    }
    if $timer>100
      $timer=0
    else
      $timer+=1
    end
  end

  def draw
    @cursor.draw
    @images.each {|img| img.draw}
    @guiWidgets.each {|widget| widget.intDraw}
    if not @state                                      ######Do not modify#######
      #@player.draw                                    #This code does avoids drawing the characters for static screens 
      @characters.sort! { |a,b| a.y<=>b.y }
      playerdrawn=false
      @characters.each { |char|
        if not playerdrawn and char.y>player.y
          @player.draw
          playerdrawn=true
        end
        char.draw(char.x-@camera.x,char.y-@camera.y)
      } #####################
      if not playerdrawn
        @player.draw
      end
      @mapimages.each { |img|
        img.draw(img.x-@camera.x,img.y-@camera.y)
      }
      @map.bg.draw(-@camera.x,-@camera.y,ZOrder::Background)
    else
      @background_image.draw(0, 0, ZOrder::Background)
      @staticObj.draw
    end                                                   ##################### 
    $framedrawn=true
 # else
    #@staticObj.draw
  end
=begin
  if @fading
    if Gosu::milliseconds % 2
      if @fadeAlpha==0
        @fading=false
        @fadeImg=nil
      else
        puts "here"
        @fadeAlpha=@fadeAlpha-15
      end
    end
    @fadeImg.draw(0,0,ZOrder::Top,1,1,Gosu::Color.new(@fadeAlpha,0,0,0))
  end
=end
end

